package org.rency.ues.engine.service.impl;

import org.rency.common.utils.security.Base64Utils;
import org.rency.common.utils.security.SHAUtils;
import org.rency.ues.common.UesDict;
import org.rency.ues.common.enums.EncryptType;
import org.rency.ues.common.enums.TicketKind;
import org.rency.ues.common.model.EncryptItem;
import org.rency.ues.domain.model.EnDataDomain;
import org.rency.ues.domain.request.DecryptBatchRequest;
import org.rency.ues.domain.request.DecryptRequest;
import org.rency.ues.domain.request.EncryptBatchRequest;
import org.rency.ues.domain.request.EncryptRequest;
import org.rency.ues.domain.response.DecryptBatchResponse;
import org.rency.ues.domain.response.DecryptResponse;
import org.rency.ues.domain.response.EncryptBatchResponse;
import org.rency.ues.domain.response.EncryptResponse;
import org.rency.ues.domain.support.Certification;
import org.rency.ues.domain.support.UesResult;
import org.rency.ues.engine.converter.EncryptConverter;
import org.rency.ues.engine.crypto.CryptoFactory;
import org.rency.ues.engine.repository.EnDataRepository;
import org.rency.ues.engine.repository.UesSequenceRepository;
import org.rency.ues.engine.service.CertificationService;
import org.rency.ues.engine.service.UesService;
import org.rency.ues.engine.validator.RequestValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;

/**
 * ${todo}
 *
 * @author: Created by user_rcy@163.com on 2016/8/2.
 * @version: V1.0.0
 * @since JDK 1.8
 */
public class UesServiceImpl implements UesService{

    private static final Logger logger = LoggerFactory.getLogger(UesServiceImpl.class);

    private CertificationService                               certificationService;

    private UesSequenceRepository                              uesSequenceRepository;

    private EnDataRepository                                   enDataRepository;

    private RequestValidator<EncryptRequest, EncryptResponse>  encryptValidator;

    private RequestValidator<DecryptRequest, DecryptResponse>  decryptValidator;

    private RequestValidator<EncryptBatchRequest, EncryptBatchResponse> encryptBatchValidator;

    private RequestValidator<DecryptBatchRequest, DecryptBatchResponse> decryptBatchValidator;

    @Override
    public EncryptResponse encrypt(EncryptRequest request) {
        logger.info("加密请求:{}.",request);
        long start = System.currentTimeMillis();
        EncryptResponse response = encryptValidator.validator(request);
        if(!response.isSuccess()){
            return response;
        }
        try {
            Certification certification = certificationService.getCertification(request.getSerialNo());
            if(certification == null){
                logger.info("未获取到证书,{}.",request.getSerialNo());
                return EncryptResponse.fail(UesDict.CHINESE_EMPTY_CERT_MSG);
            }
            UesResult uesResult = this.getPlainData(request.getEncryptType(),request.getEncryptedKey(),request.getEncryptedData(),certification);
            if(!uesResult.isSuccess()){
                logger.info("解析密文失败,{}.",uesResult);
                return EncryptResponse.fail(uesResult.getReturnMessage());
            }
            String hash = this.caculateHash(uesResult.getData());
            //生成密钥
            String ticket = uesSequenceRepository.getTicketSequence(TicketKind.P);

            EnDataDomain enDataDomain = EncryptConverter.build(ticket,request);

            boolean ret = enDataRepository.saveEnDataWithHash(hash,enDataDomain);
            if(!ret){
                logger.error("加密信息存储失败, 请求：{}.",enDataDomain);
                return EncryptResponse.fail(UesDict.CHINESE_FAIL_MSG);
            }
            response.setTicket(enDataDomain.getTicket());
            response.setEncryptKey(request.getEncryptedKey());
            logger.debug("返回加密结果:{}.",response);
            return response;
        }catch (Exception e){
            logger.error("加密请求未知异常,原始请求:{}.",request,e);
            return EncryptResponse.fail(UesDict.CHINESE_FAIL_MSG);
        }finally {
            logger.info("加密请求完成,耗时{}ms.",System.currentTimeMillis()- start);
        }
    }

    @Override
    public DecryptResponse decrypt(DecryptRequest request) {
        logger.info("解密请求:{}.",request);
        long start = System.currentTimeMillis();
        DecryptResponse response = decryptValidator.validator(request);
        if(!response.isSuccess()){
            return response;
        }
        try{
            EnDataDomain enDataDomain = enDataRepository.getByTicket(request.getTicket());
            Certification certification = certificationService.getCertification(enDataDomain.getSerialNo());
            if(certification == null){
                logger.info("未获取到证书,{}.",enDataDomain.getSerialNo());
                return DecryptResponse.fail(UesDict.CHINESE_EMPTY_CERT_MSG);
            }
            UesResult uesResult = this.getPlainData(enDataDomain.getEncryptType(),enDataDomain.getEncryptKey(),enDataDomain.getEncryptData(),certification);
            if(!uesResult.isSuccess()){
                logger.error("解密失败, 请求：{}.",request);
                return DecryptResponse.fail(UesDict.CHINESE_FAIL_MSG);
            }
            this.fillResponse(response,enDataDomain,certification,request.getTicket());
            logger.debug("返回解密结果:{}.",response);
            return response;
        }catch (Exception e){
            logger.error("解密请求未知异常,原始请求:{}.",request,e);
            return DecryptResponse.fail(UesDict.CHINESE_FAIL_MSG);
        }finally {
            logger.info("解密请求完成,耗时{}ms.",System.currentTimeMillis()- start);
        }
    }

    @Override
    public EncryptBatchResponse encryptBatch(EncryptBatchRequest request) {
        logger.info("批量加密请求:{}.",request);
        long start = System.currentTimeMillis();
        EncryptBatchResponse response = encryptBatchValidator.validator(request);
        if(!response.isSuccess()){
            return response;
        }
        try {
            Certification certification = certificationService.getCertification(request.getSerialNo());
            if (certification == null) {
                logger.info("未获取到证书,{}.", request.getSerialNo());
                return EncryptBatchResponse.fail(UesDict.CHINESE_EMPTY_CERT_MSG);
            }
            for (EncryptItem enItem : request.getEncryptItems()) {
                EncryptRequest enRequest = new EncryptRequest();
                enRequest.setSerialNo(request.getSerialNo());
                enRequest.setEncryptType(request.getEncryptType());
                enRequest.setEncryptedKey(enItem.getEncryptedKey());
                enRequest.setEncryptedData(enItem.getEncryptedData());
                EncryptResponse enResponse = this.encrypt(enRequest);
                response.addResponse(enResponse);
            }
            return response;
        }catch (Exception e){
            logger.error("批量加密请求未知异常,原始请求:{}.",request,e);
            return EncryptBatchResponse.fail(UesDict.CHINESE_FAIL_MSG);
        }finally {
            logger.info("批量加密请求完成,耗时{}ms.",System.currentTimeMillis()- start);
        }
    }

    @Override
    public DecryptBatchResponse decryptBatch(DecryptBatchRequest request) {
        logger.info("批量解密请求:{}.",request);
        long start = System.currentTimeMillis();
        DecryptBatchResponse response = decryptBatchValidator.validator(request);
        if(!response.isSuccess()){
            return response;
        }
        try{
            for(String ticket : request.getTickets()){
                DecryptRequest deRequest = new DecryptRequest();
                deRequest.setTicket(ticket);
                DecryptResponse deResponse = this.decrypt(deRequest);
                response.addResponse(deResponse);
            }
            logger.debug("返回批量解密结果:{}.",response);
            return response;
        }catch (Exception e){
            logger.error("批量解密请求未知异常,原始请求:{}.",request,e);
            return DecryptBatchResponse.fail(UesDict.CHINESE_FAIL_MSG);
        }finally {
            logger.info("批量解密请求完成,耗时{}ms.",System.currentTimeMillis()- start);
        }
    }

    /**
     * 解析密文
     * @param encryptType
     * @param encryptKey
     * @param encryptData
     * @param certification
     * @return
     */
    @Override
    public UesResult getPlainData(EncryptType encryptType, String encryptKey, String encryptData,Certification certification){
        logger.debug("解析密文:加密类型:{}, 密文Key:{}, 密文：{}.",encryptType.getCode(),encryptKey,encryptData);
        try {
            byte[] resultKey = CryptoFactory.getCryptor(encryptType).decrypt(encryptKey, new Object[]{certification});
            if (resultKey == null) {
                logger.error("工厂解密失败,返回Bytes为空");
                return UesResult.fail(String.format("工厂解密失败, %s.", encryptType.getCode()));
            }
            String keyData = null;
            try {
                keyData = new String(resultKey, UesDict.DEFAULT_CHARSET);
            } catch (UnsupportedEncodingException e) {
                logger.error("字符串转换失败, 格式不支持.", e);
                return UesResult.fail(String.format("字符串转换失败, %s.", encryptType.getCode()));
            }
            byte[] resultData = CryptoFactory.getCryptor(EncryptType.DES).decrypt(encryptData, new Object[]{keyData});
            String plainData = new String(resultData);
            UesResult uesResult = UesResult.success(plainData);
            logger.debug("解析密文结果:{}.",plainData);
            return uesResult;
        }catch (Exception e){
            logger.error("解密获取明文未知异常.",e);
            return UesResult.fail(UesDict.CHINESE_FAIL_MSG);
        }
    }

    /**
     * 计算Hash值
     * @param plainData
     * @return
     * @throws UnsupportedEncodingException
     */
    private String caculateHash(String plainData) throws UnsupportedEncodingException {
        return SHAUtils.SHA256(plainData);

    }

    private void fillResponse(DecryptResponse response,EnDataDomain enDataDomain,Certification certification,String ticket){
        response.setSerialNo(enDataDomain.getSerialNo());
        response.setEncryptType(enDataDomain.getEncryptType());
        response.setEncryptedKey(enDataDomain.getEncryptKey());
        response.setEncryptedData(enDataDomain.getEncryptData());
        response.setPrivateKey(Base64Utils.encodeBase64(certification.getPrivateKey().getEncoded()));
        response.setTicket(ticket);
    }

    public void setCertificationService(CertificationService certificationService) {
        this.certificationService = certificationService;
    }

    public void setUesSequenceRepository(UesSequenceRepository uesSequenceRepository) {
        this.uesSequenceRepository = uesSequenceRepository;
    }

    public void setEnDataRepository(EnDataRepository enDataRepository) {
        this.enDataRepository = enDataRepository;
    }

    public void setEncryptValidator(RequestValidator<EncryptRequest, EncryptResponse> encryptValidator) {
        this.encryptValidator = encryptValidator;
    }

    public void setDecryptValidator(RequestValidator<DecryptRequest, DecryptResponse> decryptValidator) {
        this.decryptValidator = decryptValidator;
    }

    public void setEncryptBatchValidator(RequestValidator<EncryptBatchRequest, EncryptBatchResponse> encryptBatchValidator) {
        this.encryptBatchValidator = encryptBatchValidator;
    }

    public void setDecryptBatchValidator(RequestValidator<DecryptBatchRequest, DecryptBatchResponse> decryptBatchValidator) {
        this.decryptBatchValidator = decryptBatchValidator;
    }
}